#ifndef CNC_UI_WINDOW_H
#define CNC_UI_WINDOW_H

// required interfaces
#include "millpdata.h"
#include "globaldata.h"
#include "progmgr.h"
#include "enventities.h"
// header files
#include <cassert>
#include <windows.h>
#include <windowsx.h>
#include "kbdcmdqueue.h"
#ifdef _DEBUG
#include <iostream>
#endif
#include <string>
#include <map>
#include "ctlcollect.h"
#include "form.h"

namespace cnc {

	namespace ui {

		class Window : public KeyboardCommandQueue
		{
		protected:
			HWND hwnd_;
			LPTSTR szClassName_;
			cnc::base::MillPersistentData *pd_;
			cnc::base::GlobalData *gd_;
			cnc::compiler::ProgramManager *pm_;
			cnc::rtl::EnvironmentEntities *rte_;
			typedef std::map<std::string, ControlCollection *> Form_;
			Form_ form_;
			FormZeroReturn formZeroReturn_;
			FormRapid formRapid_;
			FormJog formJog_;
			FormHandwheel formHandwheel_;
			FormMDI formMDI_;
			FormMemory formMemory_;
			FormEditor formEditor_;
			FormFileManager formFileManager_;
			// FormDatabaseEditor
			// FormPLCEditor
		protected:
			BOOL onCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
			{
				return TRUE;
			}
			void onCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
			{
			}
			void onDestroy(HWND hwnd)
			{
				addCommand(0xFFFF, 0xFFFF);
				//PostQuitMessage(0); // add command to kill application instead
			}
			static LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message,
				 WPARAM wParam, LPARAM lParam)
			{
				Window *wnd = 0;
#ifdef _DEBUG
				static bool alreadyCreated = false;
#endif
				if (message == WM_CREATE) {
					LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
					wnd = reinterpret_cast<Window *>(lpcs->lpCreateParams);
					SetWindowLong(hwnd, GWL_USERDATA, reinterpret_cast<LONG>(wnd));
					wnd->hwnd_ = hwnd;
#ifdef _DEBUG
					alreadyCreated = true;
#endif
				} else
					wnd = reinterpret_cast<Window *>(GetWindowLong(hwnd, GWL_USERDATA));
#ifdef _DEBUG
				std::cout << (alreadyCreated ? "*" : "") << "message = " << message;
				std::cout << "; wnd = " << wnd << std::endl;
#endif
				switch (message)
				{
					HANDLE_MSG(hwnd, WM_CREATE,  wnd->onCreate);
					HANDLE_MSG(hwnd, WM_COMMAND, wnd->onCommand);
					HANDLE_MSG(hwnd, WM_DESTROY, wnd->onDestroy);
				}
				return DefWindowProc(hwnd, message, wParam, lParam);
			}
			bool registerWndClass()
			{
				static bool registered_ = false;
				if (registered_ == true)
					return true;
				if (rte_ == 0)
					return false;
				WNDCLASSEX wndclass;
				wndclass.hInstance = rte_->getInstanceHandle();
				wndclass.lpszClassName = szClassName_;
				wndclass.lpfnWndProc = WindowProcedure;
				wndclass.style = CS_DBLCLKS;
				wndclass.cbSize = sizeof(WNDCLASSEX);
				wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
				wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
				wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
				wndclass.lpszMenuName = NULL;
				wndclass.cbClsExtra = 0;
				wndclass.cbWndExtra = 0;
				wndclass.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
				return registered_ = RegisterClassEx(&wndclass) != 0;
			}
		public:
			Window() : hwnd_(NULL), szClassName_(TEXT("Win32Desktop")),
				pd_(0), gd_(0), pm_(0), rte_(0),
				formZeroReturn_(hwnd_, LoadMenu(GetModuleHandle(NULL), TEXT("MenuZeroReturn"))),
				formRapid_(hwnd_, LoadMenu(GetModuleHandle(NULL), TEXT("MenuZeroReturn"))),
				formJog_(hwnd_, LoadMenu(GetModuleHandle(NULL), TEXT("MenuZeroReturn"))),
				formHandwheel_(hwnd_, LoadMenu(GetModuleHandle(NULL), TEXT("MenuZeroReturn"))),
				formMDI_(hwnd_, LoadMenu(GetModuleHandle(NULL), TEXT("MenuZeroReturn"))),
				formMemory_(hwnd_, LoadMenu(GetModuleHandle(NULL), TEXT("MenuZeroReturn"))),
				formEditor_(hwnd_, LoadMenu(GetModuleHandle(NULL), TEXT("MenuZeroReturn"))),
				formFileManager_(hwnd_, LoadMenu(GetModuleHandle(NULL), TEXT("MenuZeroReturn")))
			{
			}
			void setPersistentData(cnc::base::MillPersistentData *pd)
			{
				pd_ = pd;
			}
			void setGlobalData(cnc::base::GlobalData *gd)
			{
				gd_ = gd;
			}
			void setProgramManager(cnc::compiler::ProgramManager *pm)
			{
				pm_ = pm;
			}
			void setEnvironmentEntities(cnc::rtl::EnvironmentEntities *env)
			{
				rte_ = env;
			}
			bool canStart()
			{
				return pd_ != 0 && gd_ != 0 && pm_ != 0 && rte_ != 0;
			}
			bool createWindow()
			{
				if (registerWndClass() == false)
					return false;
				CreateWindowEx(0, szClassName_, TEXT("CONIN"),
#ifdef _DEBUG
					WS_OVERLAPPEDWINDOW
#else
					WS_OVERLAPPED
#endif
					| WS_CAPTION,
					0, 0, 1024, 768,
					HWND_DESKTOP, NULL,
					rte_->getInstanceHandle(), this);
				// hwnd_ must be assigned when processing WM_CREATE
				if (hwnd_ == NULL)
					return false;
				ShowWindow(hwnd_, SW_SHOWNORMAL);
				UpdateWindow(hwnd_);
				return true;
			}
			bool destroyWindow()
			{
				if (hwnd_ != NULL) {
					DestroyWindow(hwnd_);
					return true;
				}
				return false;
			}
			bool selectForm(const char *formID)
			{
				if (std::string("zeroReturnMode") == formID)
					formZeroReturn_.select();
				else
					return false;
				return true;
			}
		};

	} // namespace ui

} // namespace conin

#endif

